#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <wordexp.h>
#include "parser.h"
#include "utils.h"
#include "str_utils.h"
#include "err.h"


int current_script_argc;
char **current_script_argv;

#ifndef OWN_PARSER
#define GNU_PARSING
#else
#undef GNU_PARSING
#endif

static wordexp_t rs;
static vchar str;

void parser_init(int argc, char **argv)
{
    current_script_argc = argc;
    current_script_argv = argv;
    rs.we_wordc = 0;
    rs.we_wordv = NULL;
    vchar_init(&str);
}

#ifndef GNU_PARSING
static char *expand_variables(char* expr)
{
    vchar res;
    vchar_init(&res);
    char *str = NULL;
    int8_t detector = 0;
    do
    {
        if (!detector)
        {
            if (expr[0] == '$')
                detector = 1;
            str = strtok(expr, "$");

        }
        else
            str = strtok(NULL, "$");

        if (str != NULL)
        {
            int copy_ind = 0;
            if (detector)
            {
                char *env_value = get_env_variable(extract_var_name(str));
                if (env_value != NULL)
                {
                    copy_ind = 1;
                    str = env_value;
                }
                else
                    str = "";
            }
            vchar_append_str(&res, str);
            if (copy_ind)
                free(str);
        }
        detector = 1;
    }
    while(str != NULL);

    free(expr);
    return res.str;
}




int my_xor (int a, int b)
{
    return (!a || !b) && (a || b);
}
#endif




/*
 *reading and parsing string which passed by 1st parameter
 *or read string from stdin and parse it
 */
pprs_struct read_and_parse(char *c_str) // IF c_str == NULL read line from stdin and parse it
{
    pprs_struct res = prs_struct_init();
#ifdef GNU_PARSING
    int len = 0;

    vchar_clear(&str);
    int ch;
    do
    {
        if (c_str == NULL)
            ch = getchar();
        else
            ch = *c_str;

        if (ch == '&' || ch == '|'
                || ch == '(' || ch == ')'
                || ch == '{' || ch == '}'
                || ch == '<' || ch == '>'
                || ch == ';')
            vchar_append(&str, '\\');

        if (ch != EOF && ch != '\n' && ch != '#')
            if (c_str == NULL)
                vchar_append(&str, ch);
            else
                c_str += sizeof(char);
    }
    while (ch != EOF && ch != '\n' && ch && ch != '#');

    res->iseof = (ch == EOF);
    c_str = str.str;
    len = str.len;

    int err = (len) ? wordexp(c_str, &rs, (WRDE_UNDEF | WRDE_REUSE |
                              WRDE_SHOWERR | WRDE_NOCMD) & 0 ): 257;
    switch (err)
    {
    case WRDE_BADCHAR: print_error(make_error_struct(SHELL_PARSE_ERROR, 0, "Bad tokens"));
        break;
    case WRDE_NOSPACE: print_error(make_error_struct(SHELL_PARSE_ERROR, 0, "NO MEM"));
        break;
    case WRDE_SYNTAX: print_error(make_error_struct(SHELL_PARSE_ERROR, 0, "Syntax Error"));
    }

    if (!err)
    {
        prs_struct_reserve(res, rs.we_wordc);
        res->argc = rs.we_wordc;
        for (size_t i = 0; i < rs.we_wordc; i++)
            res->argv[i] = (rs.we_wordv[i] != NULL) ? strdup(rs.we_wordv[i]) : NULL;
    }
    wordfree(&rs);
    return res;
#else
    int8_t  parser_state = 0, delimeter_type = 0, current_delimeter_type = 0;
    vchar expr;
    vchar_init(&expr);

    res->argc = 0;
    res->__alloc_cnt = 0;
    res->argv = NULL;
    int ch;
    do
    {
        if (c_str == NULL)
            ch = getchar();
        else
            ch = *c_str;
        if (ch == '#')
            parser_state |= PRS_COMMENT_REACHED;
        if ((my_xor((delimeter_type = is_delimeter(ch)), parser_state & PRS_DELIMETER_MODE))
                || ((current_delimeter_type ^ delimeter_type) == (INCOMING_DELIMETER ^ OUTCOMING_DELIMETER))
                || ((parser_state & PRS_COMMENT_REACHED) && (expr.len)))
        {
            if (expr.len != 0)
            {
                if (!parser_state)
                {
                    expr.str = expand_variables(expr.str);
                    expr.len = expr.__alloced_len = strlen(expr.str);

                }
                //free(expr);
                //expr.str = NULL;

                prs_struct_add(&res, expr.str);
            }
            memset(expr.str, 0, sizeof(char) * expr.len);
            expr.len = 0;
            if (delimeter_type == 0 || current_delimeter_type == 0)
                parser_state ^= PRS_DELIMETER_MODE;

            current_delimeter_type = NO_DELIMETER;
        }
        if (ch != EOF && ch != '\n' && ch && (!parser_state || ch != ' ') && !(parser_state & PRS_COMMENT_REACHED))
            if (ch == '~')
            {
                vchar_append_str(&expr, getenv(HOME_VARIABLE_NAME));
            }
            else
                vchar_append(&expr, ch);

        current_delimeter_type |= delimeter_type;
        if (ch && c_str != NULL)
            c_str += sizeof(char);
    }
    while(ch != EOF && ch != '\n' && ch);

    res->iseof = (ch == EOF);
    if (expr.str != NULL)
        free(expr.str);
#endif
    return res;
}

void parser_free()
{
    wordfree(&rs);
    vchar_free(&str);
}
